/**
 * 广寒宫
 * 网址:www.guanghangong.xyz
 */
package org.moon.framework.autoconfigure.syslog;

import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.moon.framework.autoconfigure.syslog.annotation.SysLog;
import org.moon.framework.autoconfigure.utils.IpUtils;
import org.moon.framework.autoconfigure.utils.Func;
import org.moon.framework.autoconfigure.secure.AuthUtils;
import org.moon.framework.autoconfigure.syslog.domain.SysOperLog;
import org.moon.framework.autoconfigure.utils.ClassUtils;
import org.moon.framework.autoconfigure.utils.JsonUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;

import lombok.extern.slf4j.Slf4j;

/**
 * 系统日志拦截器
 * @author moon
 */
@Aspect
@Configuration
@ConditionalOnProperty(value = "moon.syslog.enabled",matchIfMissing=true)
@Slf4j
public class SysLogAutoConfigure {
	
	@Autowired
    private JdbcTemplate jdbcTemplate;
	
	private static ExecutorService logExecutorService =  Executors.newFixedThreadPool(5);


	/**
	 * 配置织入点
	 */
	@Pointcut("@annotation(org.moon.framework.autoconfigure.syslog.annotation.SysLog)")
	public void logPointCut() {
	}

	/**
	 * 前置通知 用于拦截操作
	 * 
	 * @param joinPoint
	 *            切点
	 */
	@AfterReturning(pointcut = "logPointCut()")
	public void doBefore(JoinPoint joinPoint) {
		handleLog(joinPoint, null);
	}

	/**
	 * 拦截异常操作
	 *
	 * @param joinPoint
	 * @param e
	 */
	@AfterThrowing(value = "logPointCut()", throwing = "e")
	public void doAfter(JoinPoint joinPoint, Exception e) {
		handleLog(joinPoint, e);
	}

	protected void handleLog(final JoinPoint joinPoint, final Exception e) {
		try {
			// 获得注解
			MethodSignature ms = (MethodSignature) joinPoint.getSignature();
			Method method = ms.getMethod();
			SysLog controllerLog = ClassUtils.getAnnotation(method, SysLog.class);
			if (controllerLog == null) {
				return;
			}
			String errorMsg=null;
			Integer status = 0;
			if(e!=null){
				status = 1;
				errorMsg = StringUtils.substring(e.getMessage(), 0,2000);
			}
			// 设置方法名称
			String className = joinPoint.getTarget().getClass().getName();
			String methodName = joinPoint.getSignature().getName();
			String operParam = null;
			if(controllerLog.isSaveRequestData()){
				String params="";
				if(Func.getRequest().getMethod().equalsIgnoreCase("POST")){
					Object[] paramsArray = joinPoint.getArgs();
					for (int i = 0; i < paramsArray.length; i++) {
		                String jsonObj = JsonUtils.obj2string(paramsArray[i]);
		                params += jsonObj.toString() + " ";
		            }
					params = params.trim();
				}else{
					Map<String, String[]> map = Func.getRequest().getParameterMap();
					params = JsonUtils.obj2string(map);
				}
				operParam = StringUtils.substring(params, 0, 2000);
			}
			Integer userId = 0;
			try {
				userId = AuthUtils.getCurrentUserId();
			}catch (Exception ee){
			}
			SysOperLog sysOperLog =SysOperLog.builder().status(status)
					.userId(userId)
					.operIp(IpUtils.getIpAddr(Func.getRequest()))
					.operUrl(Func.getRequest().getRequestURI())
					.errorMsg(errorMsg).method(className + "." + methodName + "()")
					.module(controllerLog.mod()).fun(controllerLog.fun()).operParam(operParam)
					.build();
			// 保存数据库
			logExecutorService.submit(new Runnable() {
				@Override
				public void run() {
					try {
						jdbcTemplate.update("insert into sys_oper_log (fun, method, userId, operUrl, operIp, operParam, errorMsg, status,operTime,module)"
								+ " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", new PreparedStatementSetter(){
							@Override
							 public void setValues(PreparedStatement ps) throws SQLException {  
								 ps.setString(1, sysOperLog.getFun());
								 ps.setString(2, sysOperLog.getMethod());
								 ps.setInt(3, sysOperLog.getUserId()==null?0:sysOperLog.getUserId());
								 ps.setString(4, sysOperLog.getOperUrl());
								 ps.setString(5, sysOperLog.getOperIp());
								 ps.setString(6, sysOperLog.getOperParam());
								 ps.setString(7, sysOperLog.getErrorMsg());
								 ps.setInt(8, sysOperLog.getStatus());
								 ps.setTimestamp(9, new Timestamp(System.currentTimeMillis()));
								 ps.setString(10, sysOperLog.getModule());
							}
						});
					} catch (DataAccessException e) {
						log.error("异步写入日志失败:"+e.getMessage(),e);
					}
				}
			});
		} catch (Exception exp) {
		}
	}
}